import copy
from typing import List, Dict, Tuple
from collections import OrderedDict

import json
import bisect

from PyQt6.sip import voidptr

from entry.Job import Job, Operation, ScheduleAssignment, GCLEAN_TYPES
from entry.WaferCassette import WaferCassette, Task
from entry.Machine import Machine, MachinesResults
from entry.DataGenerator import DataGenerator, MoveType

from entry.Constant import Parameter



class Sequence:
    def __init__(self):
        self.job: Job = Job()
        self.name: str = ""
        self.machines_clean_needed: List[str] = []  # 需要清洁的机器列表

class NoWaitJobShop:
    """一个任务中有不同路径的工件"""

    def __init__(self, job: Job = None, task: Task = None):
        self.job_type: str = ""
        self.job_operations: List[Operation] = []
        self.wafer_cassettes: List[WaferCassette] = [WaferCassette() for _ in range(3)]
        self.schedule_assignments: List[ScheduleAssignment]  = []
        self.machines: List[Machine] = []  #
        self.machines_maps: Dict[str, Machine] = {}  #
        self.job_array: List[List[str]] = [["" for _ in range(25)] for _ in range(3)]
        self.job_sequence_list: List[str] = []
        self.job_clean_sequence_list: List[str] = []
        self.machines_results: List[MachinesResults] = []

        self.makespan: float = Parameter.INFINITY
        self.makespan_best: float = Parameter.INFINITY

        self.schedule_assignments_best: List[ScheduleAssignment] = []  #
        self.job_schedule_assignments_map: Dict[str, List[ScheduleAssignment]] = {}  #
        self.machine_schedule_assignments_map: Dict[str, List[ScheduleAssignment]] = {}  #

        self.squence_heur: List[Sequence] = []  #
        self.dataGenerator = DataGenerator()

        # 有参构造
        if job and task:
            self.job_type = job.name
            self.job_operations = job.operations if job.operations else []

            # 处理晶圆盒
            if task.waferCassettes and len(task.waferCassettes) == 3:
                self.wafer_cassettes = task.waferCassettes

            # 工件数组
            self.job_array: List[List[str]] = [[f"{i + 1}.{j + 1}" for j in range(25)] for i in range(3)]


    def sequencing(self) -> int:
        """工件排序"""

        for i in range(len(self.job_array)):
            for j in range(len(self.job_array[i])):
                if self.job_array[i][j] != "0":
                    self.job_sequence_list.append(self.job_array[i][j])
        return 1

    def sequence_insert_job_cleans(self, job_sequence_list: List[str], clean_vecs: List[str], clean_insert_between_nums: List[int]) -> List[str]:
        """工件序列中 加入 清洁操作"""

        job_clean_sequence_list: List[str] = []
        count = 0  # 统计已处理的工件数量

        # 处理输入参数不一致的情况
        if len(clean_vecs) != len(clean_insert_between_nums):
            raise ValueError("清洗操作列表和插入间隔列表长度必须一致")

        # 遍历原始工件序列
        for job in job_sequence_list:
            count += 1
            job_clean_sequence_list.append(job)

            # 检查是否需要插入清洗操作
            for i in range(len(clean_insert_between_nums)):
                # 插入间隔：如间隔=2，那么当前工件序号是2的倍数时，需要添加清洁操作
                insert_interval = clean_insert_between_nums[i]
                if insert_interval <= 0:
                    raise ValueError(f"插入间隔必须为正整数，当前值: {insert_interval}")

                if count % insert_interval == 0:
                    # 插入第i个清洁操作
                    job_clean_sequence_list.append(clean_vecs[i])

        return job_clean_sequence_list

    def machineInit(self) -> int:
        """初始化前置机器：每个任务都必须的前置加工步骤"""

        # LP：装卸位  TM1：转移模块1  AL：校准模块
        machine_names = ["LP", "TM1", "AL"]
        for name in machine_names:
            machine = Machine()
            machine.set_name(name)
            self.machines.append(machine)
            self.machines_maps[name] = machine

        return 1

    def operationInit(self, operations: List[Operation]) -> int:
        """
        初始化完整工序：前置工序 + 加工工序 + 后置工序
        前置工序：晶圆从装卸位LP运送到LL真空锁，开始加工

        传参：工件加工工序列表
        返回值：工件完整工序列表
        """

        operationTemp: List[Operation] = operations
        operations.clear()

        '''初始工序：装卸位取晶圆'''
        op1 = Operation()
        op1.machine_names.append("LP")
        op1.processing_times.append(1.0)
        op1.move_types.append(MoveType.PREPARE_MOVE.value)   #LP开阀门
        op1.move_times.append(1.0)
        operations.append(op1)

        '''运输工序：LP to AL by TM1'''
        op2 = Operation()
        op2.machine_names.append("TM1")
        op2.processing_times.append(Parameter.TAKE_TIME_TM1 + 1.0 + Parameter.PUT_TIME_TM1)
        op2.move_types.append(MoveType.PICK_MOVE.value)   #TM1 从LP取晶圆
        op2.move_times.append(Parameter.TAKE_TIME_TM1)
        op2.move_types.append(MoveType.TRANS_MOVE.value)  #TM1运晶圆到AL
        op2.move_times.append(1.0)
        op2.move_types.append(MoveType.PLACE_MOVE.value)  #TM1 放晶圆
        op2.move_times.append(Parameter.PUT_TIME_TM1)
        operations.append(op2)

        '''校准工序：AL校准8s'''
        op3 = Operation()
        op3.machine_names.append("AL")
        op3.processing_times.append(8.0)
        op3.move_types.append(MoveType.ALIGN_MOVE.value)  # 校准操作
        op3.move_times.append(8.0)
        operations.append(op3)

        '''运输工序：AL to LLA/LLB by TM1'''
        op4 = Operation()
        op4.machine_names.append("TM1")
        op4.processing_times.append(Parameter.TAKE_TIME_TM1 + 1.0 + Parameter.PUT_TIME_TM1)
        op4.move_types.append(MoveType.PICK_MOVE.value)
        op4.move_times.append(Parameter.TAKE_TIME_TM1)
        op4.move_types.append(MoveType.TRANS_MOVE.value)
        op4.move_times.append(1.0)
        op4.move_types.append(MoveType.PLACE_MOVE.value)
        op4.move_times.append(Parameter.PUT_TIME_TM1)
        operations.append(op4)

        for op in operationTemp:
            operations.append(op)   # 上述前置准备工序后 添加工件正式加工工序


        """
        后置工序：晶圆加工完毕，从LL运送回装卸位LP
        """

        '''运输工序：LL to LP by TM1'''
        op5 = Operation()
        op5.machine_names.append("TM1")
        op5.processing_times.append(Parameter.TAKE_TIME_TM1 + 1.0 + Parameter.PUT_TIME_TM1)
        op5.move_types.append(MoveType.PICK_MOVE.value)
        op5.move_times.append(Parameter.TAKE_TIME_TM1)
        op5.move_types.append(MoveType.TRANS_MOVE.value)
        op5.move_times.append(1.0)
        op5.move_types.append(MoveType.PLACE_MOVE.value)
        op5.move_times.append(Parameter.PUT_TIME_TM1)
        operations.append(op5)

        # 返回1，表示初始化成功
        return 1

    def operationInitNoTM(self, operations: List[Operation]) -> int:
        """
            初始化完整工序（不带TM）：前置工序 + 加工工序 + 后置工序
            前置工序：晶圆从装卸位LP运送到LL真空锁，开始加工
        """

        operationTemp: List[Operation] = operations
        operations.clear()

        '''初始工序：装卸位取晶圆'''
        op1 = Operation()
        op1.machine_names.append("LP")
        op1.processing_times.append(1.0)
        op1.move_types.append(MoveType.PREPARE_MOVE.value)  # LP开阀门
        op1.move_times.append(1.0)
        operations.append(op1)

        '''运输工序：LP to AL by TM1'''
        # 跳过运输工序

        '''校准工序：AL校准8s'''
        op3 = Operation()
        op3.machine_names.append("AL")
        op3.processing_times.append(8.0)
        op3.move_types.append(MoveType.ALIGN_MOVE.value)  # 校准操作
        op3.move_times.append(8.0)
        operations.append(op3)

        '''运输工序：AL to LLA/LLB by TM1'''
        # 跳过运输工序

        for op in operationTemp:
            operations.append(op)  # 上述前置准备工序后 添加工件正式加工工序

        """
        后置工序：晶圆加工完毕，从LL运送回装卸位LP
        """

        '''运输工序：LL to LP by TM1'''
        # 跳过运输工序

        # 返回1，表示初始化成功
        return 1

    def time_tabling(self,sequences: List[str],
                     operations: List[Operation],
                     makespan: float,
                     scheduleResultsRecord: List[ScheduleAssignment]) -> int:
        """
        柔性作业调度车间：
        用于根据工序序列和机器信息进行调度并生成调度结果 时间表
        :param sequences: 插入清洁操作的工件处理序列
        :param operations: 工序列表，一个工件的完整工序
        :param makespan: 输出参数，最大完工时间
        :param scheduleResultsRecord: 输出参数，调度结果记录
        :return: 1表示成功，0表示失败
        """

        # 初始变量
        # 工序列表，第一层是邻域分配方法，第二层是只保留一个机器的operation序列
        lns_operations: List[List[Operation]] = []
        schedule_result: List[ScheduleAssignment] = []  # 临时调度结果
        machine_map: Dict[str, Machine] = {}  # 机器字典
        machines_result = MachinesResults()

        # 深拷贝初始机器状态
        machine_map = copy.deepcopy(self.machines_maps)

        # 多机器工序拆分成单机器工序：只保留一个可用机器的operation序列
        for op in operations:
            lns_operation: List[Operation] = []  # lns_operations的内层列表：单机器序列
            # 遍历每个工序可用机器
            for idx in range(len(op.machine_names)):
                # 单机器工序
                new_op = Operation()
                new_op.id = op.id
                new_op.machine_name = op.machine_names[idx]
                new_op.processing_times = op.processing_times[idx]

                lns_operation.append(new_op)

            lns_operations.append(lns_operation)

        # 复制 加工工件顺序 序列
        sequence: List[str] = []   # 存储的是工件名称str
        for se in sequences:
            sequence.append(se)

        # 遍历每个工件列表（含清洁操作）
        for i in range(len(sequence)):
            # 打印日志
            print(f"第{i}个工件的id是：{sequence[i]}")
            print(f"序列长度：{len(sequence)}")

            processing_time_sum = 0.0
            machine_Vec_map_temp: List[Dict[str, Machine]] = []  # 临时机器状态表
            makespan_partial: List[float] = []   # 完工时间

            # 处理非清洁类型工件
            if sequence[i] not in GCLEAN_TYPES:
                # 遍历工件i所有工序
                for j in range(len(operations)):
                    # 第1个工件的第1个工序
                    if i==0 and j==0:
                        print(f"第{sequence[i]}个工件的第{j}个工序")
                        # 第1个工件第1个工序：安排到第一个可用机器上，加工时间从0开始
                        machine = machine_map[operations[j].machine_names[0]]    # 该工序的第一台可用机器
                        machine.productTimes.append((0.0,operations[j].processing_times[0]))    # 加工时间段

                        # 调度结果
                        schedule_assignment = ScheduleAssignment()
                        schedule_assignment.move_id = i+j+1;   # move编号：所有工件所有工序编号
                        schedule_assignment.mat_id = sequence[i]
                        schedule_assignment.move_type = MoveType.PREPARE_MOVE.value
                        schedule_assignment.module_name = operations[j].machine_names[0]  # 第一台机器名称
                        schedule_assignment.start_time = machine_map[operations[j].machine_names[0]].productTimes[-1][0]
                        schedule_assignment.end_time = machine_map[operations[j].machine_names[0]].productTimes[-1][1]
                        schedule_assignment.slot_id = 1

                        schedule_result.append(schedule_assignment)

                    # 第一个工件的其他工序
                    elif i==0 and j!=0:
                        print(f"第{sequence[i]}个工件的第{j}个工序")
                        # 当前工序开始全局时间：前一个工序结束时间
                        prev_machine_name = operations[j-1].machine_names[0]  # 前一个工序（第1个工件的第1个工序）安排的加工机器
                        cur_start_time = machine_map[prev_machine_name].productTimes[-1][1]

                        # 当前工序机器的加工时间段：选工序可用机器列表的第一个加工
                        cur_machine = machine_map[operations[j].machine_names[0]]
                        cur_machine.productTimes.append((cur_start_time,cur_start_time+operations[j].processing_times[0]))

                        # 当前工序中每一步【操作】开始执行的时间：从当前工序开始执行为起点
                        timeSum = 0.0
                        # 遍历工序的多个操作：如取+运+放
                        for mv_idx in range(len(operations[j].move_types)):
                            mv_type = operations[j].move_types[mv_idx]  # 当前操作类型
                            mv_time = operations[j].move_times[mv_idx]  # 当前操作的执行时间（工序中的每一个操作）
                            schedule_assignment = ScheduleAssignment()
                            schedule_assignment.move_id = i + j + mv_type + 1
                            schedule_assignment.mat_id = sequence[i]
                            schedule_assignment.move_type = operations[j].move_types[mv_idx]
                            schedule_assignment.module_name = operations[j].machine_names[0]
                            schedule_assignment.start_time = cur_start_time + timeSum    # 当前操作全局开始时间：工序开始时间+操作开始时间
                            schedule_assignment.end_time = cur_start_time + timeSum + mv_time  # 当前操作全局结束时间：工序开始时间 + 操作开始时间 + 操作执行时间
                            schedule_assignment.slot_id = 1

                            schedule_result.append(schedule_assignment)
                            timeSum += mv_time      # 当前移动类型的时间

                    # 其他工件工序：非第一个工件
                    else:
                        print(f"第{sequence[i]}个工件的第{j}个工序")

                        # 遍历工序可用机器
                        for m in range(len(operations[j].machine_names)):
                            # 机器名称
                            cur_machine = machine_map[operations[j].machine_names[m]]
                            # 记录机器临时状态：将当前工序安排在当前机器上
                            machines_map_temp: Dict[str, Machine] = {}
                            # 当前机器的生产时间不为空：已经有工序用过
                            if cur_machine.productTimes:
                                # 复制当前机器状态：创建该对象的深拷贝
                                machine_temp = copy.deepcopy(cur_machine)

                                # 安排当前工序时间：当前工序安排到该机器上，挨着前面的工序放
                                start = machine_temp.productTimes[-1][1]   # 开始时间：当前机器最后一次执行加工的结束时间
                                end = start + operations[j].processing_times[m]   # 结束时间： 开始时间+ 当前操作在第m台机器加工的执行时间
                                machine_temp.productTimes.append((start, end))
                                machines_map_temp[operations[j].machine_names[m]] = machine_temp

                                # 剩下工序根据当前工序时间窗口反推，如果冲突，说明当前时间窗口不合适
                                is_overlap = False
                                time_sum = 0.0      # 前序工序k(结束时间) 至当前工序(开始时间)的累计加工时间
                                # 向前检查工序( j-1 到 1 )
                                for k in range(j-1, 0, -1):
                                    # 计算前序工序时间窗口
                                    end2 = machine_temp.productTimes[-1][0] - time_sum      # 结束时间：当前工序j的开始时间 - k至当前工序的累计加工时间
                                    # TODO：为啥是第一个加工时间？直接将前序工序安排在机器0？
                                    start2 = end2 - operations[k].processing_times[0]       # 开始时间：k的结束时间-工序k的自身加工时间

                                    if start2 < 0 or end2 < 0:
                                        is_overlap = True
                                        break

                                    time_sum += operations[k].processing_times[0]
                                    # 前序工序时间窗口
                                    time_slot1 = (start2, end2)
                                    # 遍历前序工序的所有可用机器l：判断倒推的前序工序时间窗口是否与前序工序的机器已有执行时间冲突
                                    for l in range(len(operations[k].machine_names)):
                                        # 冲突机器数量：
                                        unfeasible_counter = 0

                                        # 遍历前序工序的可用加工机器l 的 所有加工时间
                                        for prod_time in machine_map[operations[k].machine_names[l]].productTimes:
                                            # 检查两个时间区间是否重叠：当前工序分配的时间窗口  vs  其余机器已有的时间窗口
                                            if self.is_overlap(time_slot1, prod_time):
                                                # 重叠：说明与机器l有冲突
                                                unfeasible_counter += 1
                                                break

                                        # 前序工序时间窗口与所有可用的机器l都冲突：说明该时间窗口不合适
                                        if unfeasible_counter == len(operations[k].machine_names):
                                            is_overlap = True
                                            break
                                        # 记录可行机器状态：前序工序k倒推的时间窗口 安排在机器l上可行
                                        else:
                                            # TODO：为啥源代码要循环记录？
                                            new_machine = Machine(name=operations[k].machine_names[l])
                                            new_machine.productTimes.append((start2,end2))
                                            machines_map_temp[operations[k].machine_names[l]] = new_machine

                                    # 前序工序与当前工序安排的时间窗口冲突
                                    if is_overlap:
                                        break

                                # 将当前工序j 安排在当前机器 m上导致前序工序冲突：跳过当前机器
                                if is_overlap:
                                    continue

                                # 将当前工序j安排在当前机器m上可行：向后检查
                                time_sum = 0.0      # 现在记录的是当前工序结束 到后续工序开始的累计时间
                                for k in range(j + 1, len(operations)):
                                    # 计算后续工序时间窗口
                                    start_time = machines_map_temp[operations[j].machine_names[m]].productTimes[-1][1] + time_sum
                                    end_time = start_time + operations[k].processing_times[0]
                                    time_sum += operations[k].processing_times[0]
                                    # 后续工序的加工时间窗口
                                    time_slot1 = (start_time, end_time)

                                    # 检查后续工序的所有可选机器是否冲突
                                    # 后续工序安排在机器l上加工
                                    for l in range(len(operations[k].machine_names)):
                                        # 记录冲突的机器数量
                                        feasible_counter = 0

                                        # 检查与已有加工时间重叠
                                        for prod_time in machine_map[operations[k].machine_names[l]].productTimes:
                                            # 与机器l已有时间窗口冲突，该后续工序不能安排在机器l上
                                            if self.is_overlap(time_slot1, prod_time):
                                                feasible_counter += 1
                                                break

                                        # 后续工序时间窗口与 工序所有可用机器都冲突：说明当前时间窗口不可用
                                        if(feasible_counter == len(operations[k].machine_names)):
                                            is_overlap = True
                                            break
                                        else:
                                            # TODO：为啥不在else里记录机器状态
                                            # 记录机器：后续工序时间窗口与l无冲突，可以安排在该机器上
                                            new_machine = Machine(name=operations[k].machine_names[l])
                                            new_machine.productTimes.append(time_slot1)
                                            machines_map_temp[operations[k].machine_names[l]] = new_machine

                                    # 有重叠：后续工序与所有可用机器都冲突
                                    if is_overlap:
                                        break
                                    if k == len(operations)-1 :
                                        # 遍历到最后一个工序，记录最后一个工序结束时间
                                        makespan_partial.append(end_time)

                                # 将当前工序j安排在当前机器m上导致后续工序冲突：跳过当前机器
                                if is_overlap:
                                    continue

                                # 能到这里，说明当前工序可以安排在机器m上，没有冲突
                                # TODO：记录该工序相邻的makespan：最后一个工序的结束时间和解？？
                                machine_Vec_map_temp.append(machines_map_temp)
                            else:
                                # TODO：当前机器m生产时间为空，不处理？？
                                print("当前机器m生产时间为空")

                    print("log：准备进入剩下的工作")

            # 执行空闲清洁
            elif sequence[i] == GCLEAN_TYPES[0]:
                # 清洁工序
                print(f"处理清洁工序: {sequences[i]}")
                for machine in machine_map.values():
                    if machine.productTimes:
                        # 安排清洁时间
                        last_end = machine.productTimes[-1][1]   # 该机器最后一个工序的结束时间
                        start = last_end + Parameter.CLEAN_FREE_WAIT_TIME   # 清洁开始时间：工序结束时间 + 空闲时间
                        end = start + Parameter.CLEAN_FREE_EXECUTION_TIME   # 清洁结束时间：清洁开始时间 + 清洁执行时间
                        # 该机器的执行时间段中插入清洁时间段
                        machine.productTimes.append((start, end))

            # 选择最优调度方案：记录的临时makespan中找一个最小的，作为最终解
            if len(makespan_partial)>0 and i!=0 and sequence[i] != GCLEAN_TYPES[0]:
                # 最小完工时间的索引
                min_idx = makespan_partial.index(min(makespan_partial))
                # 最优调度方案用到的机器列表
                selected_machines = machine_Vec_map_temp[min_idx]

                # 更新机器状态和调度结果
                for j in range(len(operations)):
                    for m in range(len(operations[j].machine_names)):
                        # 最优调度方案 包含 当前机器
                        machine_name = operations[j].machine_names[m]
                        if machine_name in selected_machines:
                            # 更新机器加工时间
                            machine_map[machine_name].productTimes = [
                                *selected_machines[machine_name].productTimes
                            ]

                            # 记录工序里的每一个操作
                            time_sum = 0.0   # 当前工序开始时间至 当前操作开始时间的间隔
                            for op_mv_idx in range(len(operations[j].move_types)):
                                mv_type = operations[j].move_types[op_mv_idx]
                                mv_time = operations[j].move_types[op_mv_idx]
                                # 最后一次加工的开始时间
                                last_start = selected_machines[machine_name].productTimes[-1][0]

                                schedule_assignment = self.record_schedule_assignment(
                                    move_id= j+m,
                                    # TODO：移动编号为啥是j+m?  移动类型不应该是move_type[mv]
                                    move_type=operations[j].move_types[m],
                                    module_name= operations[j].machine_names[m],
                                    mat_id= sequences[i],
                                    start_time= last_start + time_sum,
                                    end_time= last_start + time_sum + mv_time,
                                    slot_id= 1
                                )
                                schedule_result.append(schedule_assignment)
                                time_sum += mv_time
                            break  # 只处理第一个可行机器
            elif len(makespan_partial)==0 and i!=0 and sequences[i] != GCLEAN_TYPES[1]:
                # 无可行方案
                return 0

        # 整理最终结果
        for schedule in schedule_result:
            scheduleResultsRecord.append(schedule)

        machines_result.schedule_results = scheduleResultsRecord
        machines_result.makespan = schedule_result[-1].end_time
        machines_result.machinesMap = machine_map

        makespan = machines_result.makespan
        self.machines_results.append(machines_result)

        return 1


    def execute_scheduling(self) -> int:
        # 实现调度执行逻辑
        return 0

    def is_overlap(self, time_slot1: Tuple[float, float], time_slot2: Tuple[float, float]) -> bool:
        """ 判断两个时间区间是否重叠 """

        start1, end1 = time_slot1       # slot1：先加工区间
        start2, end2 = time_slot2       # slot2：后加工区间
        return start1 < end2 and start2 < end1

    def record_schedule_assignment(
            self,
            move_id: int,
            move_type: int,
            module_name: str,
            mat_id: str,
            start_time: float,
            end_time: float,
            slot_id: int
    ) -> ScheduleAssignment:
        """创建并记录一个工序的调度结果"""

        # 检查时间有效性
        if end_time < start_time:
            raise ValueError(f"结束时间：{end_time}， 不能早于开始时间：{start_time}")

        # 创建一个工序的调度分配
        assignment = ScheduleAssignment()
        assignment.move_id = move_id  # 工件id
        assignment.mat_id = mat_id  # 物料编号
        assignment.move_type = move_type  # move类型
        assignment.module_name = module_name  # 模块名称
        assignment.start_time = start_time  # move开始时间
        assignment.end_time = end_time  # move结束时间
        assignment.slot_id = slot_id  # 槽位id

        return assignment

    def write_json(self, file_name: str, schedule_results: List[ScheduleAssignment]) -> int:
        """将调度结果保存为JSON文件"""

        # 创建有序字典
        json_data = OrderedDict()
        json_data["MoveList"] = []

        # 遍历调度结果，转为有序字典
        for schedule in schedule_results:
            move_item = OrderedDict([
                ("StartTime", schedule.start_time),
                ("EndTime", schedule.end_time),
                ("MoveID", schedule.move_id),
                # TODO：move_type 枚举类
                ("MoveType", schedule.move_type),
                ("ModuleName", schedule.module_name),
                ("MatID", schedule.mat_id),
                ("SlotID", schedule.slot_id)
            ])
            json_data["MoveList"].append(move_item)

        try:
            with open(file_name, "w", encoding="utf-8") as f:
                json.dump(json_data, f, ensure_ascii=False, indent=4)
            return 1
        except Exception as e:
            print(f"无法打开文件：{file_name}，错误信息：{e}")
            return 0


    def machineInit(self, sequences: List[Sequence], machines: List[Machine], machines_map: Dict[str,Machine]):
        """初始化前置机器"""

        for seq in sequences:
            for operation in seq.job.operations:
                # 遍历工序允许使用的所有机器名称
                for machine_name in operation.machine_names:
                    # 若机器名不在映射中，则创建新机器并添加
                    if machine_name not in machines_map:
                        # 创建Machine对象并设置名称
                        machine = Machine()
                        machine.set_name(machine_name)
                        # 添加到机器列表和映射表
                        machines.append(machine)
                        machines_map[machine_name] = machine

    def heuristics_5a(self) -> List[Sequence]:
        """ 任务5a：生成启发式作业序列"""

        sequence_heur = []  # 作业序列
        job_a = self.dataGenerator.generate_job_a_no_tm()  # 不含运输工序的作业a
        self.operationInitNoTM(job_a.operations)   # 初始化完整工序（不包括运输）

        clean_interval_num = 2    # 清洁间隔
        inserted_count = 0      # 记录加工作业数量

        # 3*25个作业
        for i in range(1, 4):
            for j in range(1, 26):
                # 加工作业
                seq = Sequence()
                seq.name = f"{i}.{j}"
                seq.job = job_a
                sequence_heur.append(seq)
                inserted_count += 1

                # 判断是否需要清洁
                if inserted_count % clean_interval_num == 0 and inserted_count != 75:
                    # 清洁作业
                    seq_clean = Sequence()
                    seq_clean.name = GCLEAN_TYPES[0]  # 空闲清洁
                    seq_clean.machinesCleanNeeded = ["PM7", "PM8"]  # 需要清洁的机器列表
                    sequence_heur.append(seq_clean)

        return sequence_heur

    def heuristics_5b(self) -> List[Sequence]:
        """
        任务5b：生成作业序列
        相对于5a, 5b工序的可用机器多于一个，所以有两种交替工序模式
        平衡分配两个模式，避免因集中在一台机器上加工导致的调度偏差
        """
        sequence_heur = []
        job_b = self.dataGenerator.generate_job_b()
        self.operationInit(job_b.operations)

        """两种交替工序模式"""
        # 模式1：使用每个工序的第一个可用机器和第一个加工时间
        opPattern1: List[Operation] = []
        for operation in job_b.operations:
            op = Operation()
            op = operation
            op.machine_names.clear()
            op.machine_names.append(operation.machine_names[0])
            op.processing_times.clear()
            op.processing_times.append(operation.processing_times[0])
            opPattern1.append(op)

        # 模式2：使用每个工序的最后一个可用机器和最后一个加工时间
        opPattern2: List[Operation] = []
        for operation in job_b.operations:
            op = Operation()
            op.machine_names.clear()
            op.machine_names.append(operation.machine_names[-1])
            op.processing_times.clear()
            op.processing_times.append(operation.processing_times[-1])
            opPattern2.append(op)

        # TODO：清洁间隔是怎么设置的？？
        clean_interval_num = 24  # 清洁间隔
        inserted_count = 0  # 记录加工作业数量

        for i in range(1, 4):
            for j in range(1, 26):
                seq = Sequence()
                seq.name = f"{i}.{j}"
                seq.job = job_b
                """交替使用两种工序模式：相邻两个工件，一个在模式1加工，一个在模式2加工"""
                if inserted_count % 2 == 1 :
                    # 奇数工序：模式1
                    seq.job.operations = opPattern1
                else:
                    # 偶数工序：模式2
                    seq.job.operations = opPattern2

                sequence_heur.append(seq)
                inserted_count += 1

                # 判断是否达到清洁间隔：添加清洁操作
                if inserted_count % clean_interval_num == 0 and inserted_count != 75:
                    seq_clean = Sequence()
                    seq_clean.name = GCLEAN_TYPES[0]
                    # TODO：一旦触发清洁，所有加工机器都清洁？？
                    seq_clean.machinesCleanNeeded = ["PM7", "PM8", "PM1", "PM2"]
                    sequence_heur.append(seq_clean)

        return sequence_heur


    def heuristics_5c(self) -> List[Sequence]:
        """
        生成任务5C的启发式调度序列
        晶圆盒1：工件C
        晶圆盒2：工件D
        晶圆盒3：工件D
        """

        # 存储任务c的最终工序序列：所有工件的所有工序（包括清洁）
        squence_heur: List[Sequence] = []

        # 生成工件C D
        job_c = self.dataGenerator.generate_job_c()
        job_d = self.dataGenerator.generate_job_d()
        self.operationInit(job_c.operations)
        self.operationInit(job_d.operations)

        # 存储JobD和JobC的工序方案
        opPatternD: List[List[Operation]] = []  # JobD的工序方案（2种）
        opPatternC: List[List[Operation]] = []  # JobC的工序方案（4种）

        '''
        生成JobD的2种工序方案：D每个工序最多有两个可选机器
        方案1：选用第一个机器加工
        方案2：选用第二个机器加工
        '''
        for k in range(2):
            opPattern1: List[Operation] = []    # 存储第k种方案的工序序列
            for l in range(len(job_d.operations)):
                op = Operation()
                op = job_d.operations[l]     # 复制 工件D每个工序
                machine_num = len(job_d.operations[l].machine_names)  # 工序可用机器数量

                # 清空可用机器列表
                op.machine_names.clear()
                op.machine_names.append(job_d.operations[l].machine_names[k % machine_num])   # 选择第 k%name_num个 机器
                op.processing_times.clear()
                op.processing_times.append(job_d.operations[l].processing_times[k % machine_num])

                opPattern1.append(op)
            opPatternD.append(opPattern1)

        '''生成JobC的4种工序方案：C中每个工序最多有四个可选机器'''
        for k in range(4):
            opPattern1: List[Operation] = []
            for l in range(len(job_c.operations)):
                op = Operation()
                op = job_c.operations[l]
                machine_num = len(op.machine_names)

                # 清空可用机器列表
                op.machine_names.clear()
                op.machine_names.append(job_c.operations[l].machine_names[k % machine_num])  # 选择第 k%name_num个 机器
                op.processing_times.clear()
                op.processing_times.append(job_c.operations[l].processing_times[k % machine_num])

                opPattern1.append(op)
            opPatternC.append(opPattern1)

        # 清洁间隔和计数器
        clean_interval_num = 24
        inserted_count = 0

        # 生成整个任务的调度序列
        for i in range(1, 4):
            for j in range(1, 26):
                seq = Sequence()

                # 晶圆盒1
                if i == 1:
                    # TODO：晶圆盒子1 为啥命名格式："3.j"
                    seq.name = f"{i + 2}.{j}"
                    # TODO：晶圆盒1 不是工件C吗？？
                    seq.job = job_d

                    # 从4种方案里，选择该工件的调度序列
                    if j % 4 == 0:
                        seq.job.operations = opPatternD[0]
                    elif j % 4 == 1:
                        seq.job.operations = opPatternD[1]
                    #
                    else:
                        # TODO : 工件C 4种调度方案
                        seq.job.operations = opPatternD[0] if j % 4 == 2 else opPatternD[1]

                # TODO：晶圆盒2 3 : 不是工件D吗？
                else:
                    # 命名格式："1.j"或"2.j"（i-1=1,2）
                    seq.name = f"{i - 1}.{j}"
                    seq.job = job_c

                    # 根据j的模4结果选择JobC的工序方案
                    if j % 4 == 0:
                        seq.job.operations = opPatternC[0]
                    elif j % 4 == 1:
                        seq.job.operations = opPatternC[1]
                    elif j % 4 == 2:
                        seq.job.operations = opPatternC[2]
                    else:  # j%4==3
                        seq.job.operations = opPatternC[3]

                # 添加当前工件到调度序列
                squence_heur.append(seq)
                inserted_count += 1

                # TODO : 清洁间隔怎么算大？每24个工件，且不是最后一个工件时，插入一次清洁工序
                if inserted_count % clean_interval_num == 0 and inserted_count != 75:
                    seq_clean = Sequence()
                    seq_clean.name = GCLEAN_TYPES[0]  # 第一种清洁类型
                    # TODO：所有机器都清洁？需要清洁的机器列表
                    seq_clean.machinesCleanNeeded = [
                        "PM7", "PM8", "PM1", "PM2", "PM3", "PM4", "PM9", "PM10"
                    ]
                    squence_heur.append(seq_clean)

        return squence_heur

    def heuristics_5d(self) -> list[Sequence]:
        """
        生成任务5D的启发式调度序列
        晶圆盒1：1-10 E  11-25: F
        晶圆盒2： 1-5 G  6-15 H   16-25 I
        晶圆盒3：1-15 J  16-25  K
        """

        sequence_heur = []     # 存储任务d的调度序列

        # 初始化工件
        job_e = self.dataGenerator.generate_job_e()
        job_f = self.dataGenerator.generate_job_f()
        job_g = self.dataGenerator.generate_job_g()
        job_h = self.dataGenerator.generate_job_h()
        job_i = self.dataGenerator.generate_job_i()
        job_j = self.dataGenerator.generate_job_j()
        job_k = self.dataGenerator.generate_job_k()

        # 初始化所有工件的完整工序
        self.operationInit(job_e.operations)
        self.operationInit(job_f.operations)
        self.operationInit(job_g.operations)
        self.operationInit(job_h.operations)
        self.operationInit(job_i.operations)
        self.operationInit(job_j.operations)
        self.operationInit(job_k.operations)

        ''' 生成JobK的 4 种工序方案：K每个工序最多有四个可选机器 '''
        opPatternK = []
        for k in range(4):
            # 存储每种工序方案
            opPattern1 = []
            # 遍历工序k的所有工序
            for l in range(len(job_k.operations)):
                # 获取原始操作对象
                op = job_k.operations[l]

                # 创建原始对象的深拷贝，避免修改原始数据
                new_op = Operation()
                new_op.__dict__ = op.__dict__.copy()

                machine_num = len(new_op.machine_names)  # 工序可用机器数量

                # 清除并重新设置可用机器和处理时间
                new_op.machine_names.clear()
                new_op.machine_names.append(op.machine_names[k % machine_num])

                new_op.processing_times.clear()
                new_op.processing_times.append(op.processing_times[k % machine_num])

                opPattern1.append(new_op)
            opPatternK.append(opPattern1)

        # 清洗间隔和插入计数
        # TODO : 怎么确定的间隔
        clean_interval_num = 12
        inserted_count = 0
        """
        任务d中包括7种路径晶圆，所以工序序列分为7类
        且按照【最短加工时间】调度规则，优先加工时长最短的工件类型
        排序：I J E F G H K
        """
        seq_vec2 = [[] for _ in range(7)]

        # 3个晶圆盒
        for i in range(1, 4):
            for j in range(1, 26):
                # 作业工序
                seq = Sequence()
                '''工件E ： 晶圆盒1 1-10'''
                if i == 1 and j <= 10:
                    seq.name = f"{i}.{j}"
                    seq.job = job_e
                    # 工件e 第三个加工
                    seq_vec2[2].append(seq)
                elif i == 1 and j >= 11:
                    ''' 晶圆盒1 11-25 ： 工件F'''
                    seq.name = f"{i}.{j}"
                    seq.job = job_f
                    seq_vec2[3].append(seq)
                elif i == 2 and j <= 5:
                    ''' 晶圆盒2 1-5 ： 工件G'''
                    seq.name = f"{i}.{j}"
                    seq.job = job_g
                    seq_vec2[4].append(seq)
                elif i == 2 and j >= 6 and j <= 15:
                    ''' 晶圆盒2 6-15 ： 工件H'''
                    seq.name = f"{i}.{j}"
                    seq.job = job_h
                    seq_vec2[5].append(seq)
                elif i == 2 and j >= 16:
                    ''' 晶圆盒2 16-25 ： 工件I'''
                    seq.name = f"{i}.{j}"
                    seq.job = job_i
                    '''工件i完成时间最短，优先加工晶圆盒中的工件i'''
                    seq_vec2[0].append(seq)
                elif i == 3 and j <= 15:
                    ''' 晶圆盒3 1-15 ： 工件J'''
                    seq.name = f"{i}.{j}"
                    seq.job = job_j
                    seq_vec2[1].append(seq)
                else:
                    ''' 晶圆盒3 16-25 ： 工件K'''
                    seq.name = f"{i}.{j}"
                    seq.job = job_k
                    # 不同的工件k 选择不同的工序方案（4种）
                    seq.job.operations = opPatternK[j%4]
                    # 工件k的 完工时间最长：最后加工
                    seq_vec2[6].append(seq)

        # 创建清洗任务
        seq_clean = Sequence()
        seq_clean.name = GCLEAN_TYPES[0]
        seq_clean.machines_clean_needed.extend([
            "PM7", "PM8", "PM1", "PM2", "PM3", "PM4", "PM9", "PM10"
        ])

        # 组合最终序列
        # 1. 交替添加seqVec2[0]和seqVec2[1]：工件I  工件J
        for i in range(len(seq_vec2[0])):
            sequence_heur.append(seq_vec2[0][i])   # 工件I 一共10个
            sequence_heur.append(seq_vec2[1][i])   # 工件J 一共15个，先添加10个
        sequence_heur.append(seq_clean)

        # 2. 交替添加seqVec2[1]和seqVec2[3]的部分：工件J 工件F
        for i in range(10, 15):
            sequence_heur.append(seq_vec2[1][i])
            sequence_heur.append(seq_vec2[3][i - 10])
        sequence_heur.append(seq_clean)

        # 3. 交替添加seqVec2[3]和seqVec2[2]的部分
        for i in range(5, 15):
            sequence_heur.append(seq_vec2[3][i])
            sequence_heur.append(seq_vec2[2][i - 5])
        sequence_heur.append(seq_clean)

        # 4. 交替添加seqVec2[4]和seqVec2[5]的部分
        for i in range(5):
            sequence_heur.append(seq_vec2[4][i])
            sequence_heur.append(seq_vec2[5][i])
        sequence_heur.append(seq_clean)

        # 5. 添加seqVec2[5]的剩余部分
        for i in range(5, 10):
            sequence_heur.append(seq_vec2[5][i])
        sequence_heur.append(seq_clean)

        # 6. 添加seqVec2[6]的10个：最后处理十个工件K
        for i in range(10):
            sequence_heur.append(seq_vec2[6][i])

        return sequence_heur

    def time_tabling(self, squenceHeur: List[Sequence], machinesMaps: Dict[str,Machine], scheduleAssignments: List[ScheduleAssignment]):
        """
        根据给定的工件和清洁操作序列，为每一个操作分配机器并安排时间，生成完整的调度方案。
        :param squenceHeur: 调度序列（工件或清洁操作）
        :param machinesMaps: 机器映射表
        :param scheduleAssignments: 存储调度分配结果
        :return:
        """

        # 遍历调度序列
        for i in range(len(squenceHeur)):
            print(f"-------------1. 处理第{i}个工件的ID是{squenceHeur[i].name}")

            # 工件
            if squenceHeur[i].name not in GCLEAN_TYPES:
                if i == 0:
                    print(f"2. 处理第{i}个工件的ID是{squenceHeur[i].name}")
                    # 处理第一个工件
                    self.do_first_sequence(squenceHeur[i], machinesMaps, scheduleAssignments)
                else:
                    print(f"3. 处理第{i}个工件的ID是{squenceHeur[i].name}")
                    # 处理其他工件
                    self.do_other_sequence(squenceHeur[i], machinesMaps, scheduleAssignments)
            # 清洁
            else:
                print("--------------------------4. 清洁")
                # 遍历需要清洁的加工机器
                for cleanIdx in range(len(squenceHeur[i].machines_clean_needed)):
                    self.do_wait_clean(squenceHeur[i].machines_clean_needed[cleanIdx], machinesMaps, scheduleAssignments)

    def do_first_sequence(self, seq: Sequence, machines_map_rec: Dict[str, Machine], 
                         schedule_results_rec: List[ScheduleAssignment]) -> None:
        """ 处理调度中的第一个工件序列：第一个工件不需要考虑前面工件的完成时间约束 """

        # 上一个工序完成时间
        lastOperationEndTime = 0.0
        # 遍历工件工序
        for opIdx in range(len(seq.job.operations)):
            operation = seq.job.operations[opIdx]
            # 第一个可用机器 及其 加工时间
            operationMachine = seq.job.operations[opIdx].machine_names[0]
            prodTime = seq.job.operations[opIdx].processing_times[0]

            if opIdx == 0:
                machines_map_rec[operationMachine].productTimes.append((0.0, prodTime))
                self.operation_record_schedule_assignment(seq.name, operation,
                                                       machines_map_rec[operationMachine].productTimes[-1],
                                                       schedule_results_rec)
                lastOperationEndTime = prodTime
            else:
                machines_map_rec[operationMachine].productTimes.append(
                    (lastOperationEndTime, lastOperationEndTime + prodTime))
                self.operation_record_schedule_assignment(seq.name, operation,
                                                       machines_map_rec[operationMachine].productTimes[-1],
                                                       schedule_results_rec)
                lastOperationEndTime += prodTime


    def do_other_sequence(self, seq: Sequence, machines_map_rec: Dict[str, Machine],
                          schedule_results_rec: List[ScheduleAssignment]) -> None:
        """
        处理非首个工件序列的调度
        算法思路：固定一个工序位置，排列其他工序，寻找不重叠且总时间最短的方案
        """
        # 存储不同候选方案的机器状态和调度结果
        machinesMapTempVec = []
        scheduleResultsVec2 = []

        # 遍历当前工件的每道工序，尝试将每道工序固定在不同位置
        for opIdx in range(len(seq.job.operations)):
            # 获取当前工序的机器和加工时间
            operation = seq.job.operations[opIdx]
            operationMachineName = seq.job.operations[opIdx].machine_names[0]
            prodTime = seq.job.operations[opIdx].processing_times[0]

            lastOperationEndTime = 0.0
            is_overlap = False
            time_sum = 0.0

            # 检查目标机器是否已有加工时间段记录
            if not self.machines_maps[operationMachineName].productTimes:
                # TODO :没看懂
                is_overlap = True
                break

            machines_mpa_temp: Dict[str,Machine] = {}
            # 对目标机器上的加工时间段按开始时间升序排列
            self.sort_by_first(machines_map_rec[operationMachineName].productTimes)

            # 尝试将当前工序插入到目标机器上每个已有时间段之后
            for prodSlotTimeIdx in range(len(machines_map_rec[operationMachineName].productTimes)):
                # 初始化临时机器状态和调度结果
                scheduleResults: List[ScheduleAssignment] = []
                machines_mpa_temp = {name: self.copy_machine(machine) for name, machine in machines_map_rec.items()}

                # 计算当前工序的开始和结束时间（紧接所选时间段之后）
                lastEndTime = machines_mpa_temp[operationMachineName].productTimes[prodSlotTimeIdx][-1]
                timeSlot1 = (lastEndTime, lastEndTime + prodTime)

                # 检查时间是否合法：时间冲突，不能插入到该时间段
                if timeSlot1[0] < 0.0 or timeSlot1[1] < 0.0:
                    is_overlap = True
                    continue

                for otherSlotTimeIdx in range(len(machines_map_rec[operationMachineName].productTimes)):
                    if otherSlotTimeIdx == prodSlotTimeIdx:
                        continue  # 跳过自身
                    # 先判断该机器上是否有重叠
                    if self.is_overlap(timeSlot1, machines_map_rec[operationMachineName].productTimes[otherSlotTimeIdx]):
                        is_overlap = True
                        break

                # 将当前工序添加到机器的加工时间段列表中
                machines_mpa_temp[operationMachineName].productTimes.append(timeSlot1)
                self.sort_by_first(machines_mpa_temp[operationMachineName].productTimes)

                # 向前安排该工序之前的其他工序
                isOverlap = self.forward_operation_arrange(seq, opIdx, machines_mpa_temp, scheduleResults, timeSlot1)
                if isOverlap:
                    continue

                self.sort_by_first(machines_mpa_temp[operationMachineName].productTimes)

                # 向后安排该工序之后的其他工序
                isOverlap = self.backward_operation_arrange(seq, opIdx, machines_mpa_temp, scheduleResults, timeSlot1)
                if isOverlap:
                    continue

                # 记录机器：保存当前候选方案的机器状态和调度结果
                machinesMapTempVec.append(machines_mpa_temp)
                scheduleResultsVec2.append(scheduleResults)

        # 选择最优调度方案：
        if machinesMapTempVec:
            # 定义比较函数：基于最后一个调度任务makespan
            def compare_schedules(a, b):
                if not a or not a[-1]:
                    return False
                if not b or not b[-1]:
                    return True
                return a[-1].endTime < b[-1].endTime

            # 找出最优方案索引
            min_idx = 0
            for i in range(1, len(scheduleResultsVec2)):
                if compare_schedules(scheduleResultsVec2[i], scheduleResultsVec2[min_idx]):
                    min_idx = i

            # 更新全局机器状态和调度结果
            machines_map_rec.clear()
            machines_map_rec.update(machinesMapTempVec[min_idx])

            # 合并调度结果
            schedule_results_rec[:0] = scheduleResultsVec2[min_idx]

    def forward_operation_arrange(self, seq: Sequence, op_idx: int, machines_map_temp: Dict[str, Machine],
                                 schedule_res: List[ScheduleAssignment], time_slot: Tuple[float, float]) -> bool:
        """
        向前安排工序：从指定工序开始，倒序安排其之前的所有工序（确保无重叠）
        return False:  无冲突   return True：有冲突
        """

        # 初始化累计时间:前序工序结束时间至当前工序开始时间之间的间隔
        time_sum = 0.0
        # 获取当前工件的所有工序
        operations = seq.job.operations

        # 复制当前机器状态
        mac_map = copy.deepcopy(machines_map_temp)

        # 倒序遍历当前工序之前的所有工序
        for i in range(op_idx - 1, -1, -1):
            # 获取前序工序的加工时间、对应机器名称
            op_processing_time = operations[i].processing_times[0]
            op_machine_name = operations[i].machine_names[0]

            # 计算当前工序的结束时间和开始时间：
            end_time = time_slot[0] - time_sum     # 结束时间 = 基准工序的开始时间 - 累计时间
            start_time = end_time - op_processing_time    # 开始时间 = 结束时间 - 当前工序的加工时间


            # 检查时间是否合法
            if start_time < 0.0 or end_time < 0.0:
                return True

            # 当前（前序）工序的时间区间
            current_time_slot = (start_time, end_time)

            # 检查当前工序的时间区间是否与机器上已有时间重叠
            for prod_time in mac_map[op_machine_name].productTimes:
                if self.is_overlap(current_time_slot, prod_time):
                    return True  # 时间重叠，返回冲突

            # 若无冲突，更新机器的加工时间序列
            machines_map_temp[op_machine_name].productTimes.append(current_time_slot)
            # 按开始时间排序更新后的时间序列
            self.sort_by_first(machines_map_temp[op_machine_name].productTimes)

            # 记录当前工序的调度结果
            self.operation_record_schedule_assignment(
                seq.name,
                operations[i],
                machines_map_temp[op_machine_name].productTimes[-1],  # 刚添加的时间区间
                schedule_res
            )

            # 更新累计时间
            time_sum += op_processing_time

        # 所有前序工序安排完成且无冲突
        return False


    def backward_operation_arrange(self, seq: Sequence, op_idx: int, machines_map_temp: Dict[str, Machine],
                                   schedule_res: List[ScheduleAssignment], time_slot: Tuple[float, float]) -> bool:
        """
        向后安排工序：从指定工序开始，顺序安排其后的所有工序

        返回：
            bool: 若安排过程中出现冲突则返回True，否则返回False
        """
        # 初始化累计时间（用于计算后续工序的时间）
        time_sum = 0.0
        # 获取当前工件的所有工序
        operations = seq.job.operations

        # 顺序遍历当前工序之后的所有工序
        for i in range(op_idx + 1, len(operations)):
            # 获取当前工序的加工时间和对应机器名称
            op_processing_time = operations[i].processing_times[0]
            op_machine_name = operations[i].machine_names[0]

            # 计算当前工序的开始时间和结束时间：
            # 开始时间 = 基准工序的结束时间 + 累计时间（确保与基准工序衔接）
            # 结束时间 = 开始时间 + 当前工序的加工时间
            start_time = time_slot[1] + time_sum
            end_time = start_time + op_processing_time

            # 检查时间是否合法（不能为负数）
            if start_time < 0.0 or end_time < 0.0:
                return True  # 时间非法，返回冲突

            # 后续工序的时间区间
            current_time_slot = (start_time, end_time)

            # 检查当前工序的时间区间是否与机器上已有时间重叠
            for prod_time in machines_map_temp[op_machine_name].productTimes:
                if self.is_overlap(current_time_slot, prod_time):
                    return True  # 时间重叠，返回冲突

            # 若无冲突，更新机器的加工时间序列
            machines_map_temp[op_machine_name].productTimes.append(current_time_slot)
            # 按开始时间排序更新后的时间序列
            self.sort_by_first(machines_map_temp[op_machine_name].productTimes)

            # 记录当前工序的调度结果
            self.operation_record_schedule_assignment(
                seq.name,
                operations[i],
                machines_map_temp[op_machine_name].productTimes[-1],  # 刚添加的时间区间
                schedule_res
            )

            # 累计时间增加当前工序的加工时间（用于计算更后续的工序）
            time_sum += op_processing_time

        # 所有后续工序安排完成且无冲突
        return False

    def operation_record_schedule_assignment(self, mat_id: str, op: Operation, 
                                            time_slot: Tuple[float, float], 
                                            schedule_results: List[ScheduleAssignment]) -> None:
        # 记录工序调度分配
        pass

    def do_wait_clean(self, machine_name: str, machines_map_rec: Dict[str,Machine], schedule_results_rec: List[ScheduleAssignment]) -> None:
        """ 处理机器的清洁任务调度，在机器加工序列末尾添加清洁时间并记录 """

        # 1. 按照机器的加工时间序列的起始时间排序
        self.sort_by_first(machines_map_rec[machine_name].productTimes)

        # 2. 检查机器是否有加工记录，若有则添加清洁任务
        if machines_map_rec[machine_name].productTimes:
            # 获取最后一次加工的结束时间
            last_end_time = machines_map_rec[machine_name].productTimes[-1][1]

            # 计算清洁任务的开始和结束时间：

            # 结束时间 = 开始时间 + 清洁执行时间
            clean_start = last_end_time + Parameter.CLEAN_FREE_WAIT_TIME    # 开始时间 = 最后加工结束时间 + 清洁等待时间
            clean_end = clean_start + Parameter.CLEAN_FREE_EXECUTION_TIME

            # 3. 向机器的加工时间序列添加清洁时间段
            machines_map_rec[machine_name].productTimes.append((clean_start, clean_end))

            # 4. 记录清洁任务的调度信息
            clean_record = self.record_schedule_assignment(
                0,
                MoveType.CLEAN_MOVE.value,
                machine_name,
                GCLEAN_TYPES[0],
                clean_start,
                clean_end,
                1
            )
            schedule_results_rec.append(clean_record)

    def machine_init_with_sequences(self, sequences: List[Sequence], machines: List[Machine], 
                                   machines_maps: Dict[str, Machine]) -> None:
        # 基于序列初始化机器
        pass

    def sort_by_first(self, vec: List[Tuple[float, float]]) -> None:
        # 按元组的第一个元素升序排序
        vec.sort(key=lambda x: x[0])

